/*
 * Copyright 2023-2025 Dozingfiretruck
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "nes.h"
#include <stdint.h>
#include "plf.h"  // 包含 IRQn_Type、SPIS_IRQn 定义和 core_cm3.h

// Performance timing helper function - prioritize port-provided time function
// If not provided, use a simple counter
#ifdef NES_GET_TIME_MS
// Use port-provided time function (defined in nes_port.c)
// system_get_curr_time returns uint32_t, wraps at 83886079 (0x4FFFFFF)
extern uint32_t NES_GET_TIME_MS(void);
static uint32_t get_time_ms(void) {
    return NES_GET_TIME_MS();
}

// Calculate time difference, handle wrap-around (max value 83886079)
// system_get_curr_time returns uint32_t with max 0x4FFFFFF (83886079)
static uint32_t time_diff(uint32_t end, uint32_t start) {
    if (end >= start) {
        // Normal case: no wrap-around
        return end - start;
    } else {
        // Wrap-around occurred: max value is 83886079 (0x4FFFFFF), then wraps to 0
        // diff = (83886080 - start) + end
        return (83886080U - start) + end;
    }
}
#elif defined(_WIN32)
#include <windows.h>
static uint64_t get_time_ms(void) {
    return GetTickCount64();
}
static uint64_t time_diff(uint64_t end, uint64_t start) {
    return end - start;
}
#else
// Use simple counter for relative time measurement
// Note: This is only for relative time comparison, not real time
static uint64_t get_time_ms(void) {
    static uint64_t counter = 0;
    return ++counter;
}
static uint64_t time_diff(uint64_t end, uint64_t start) {
    return end - start;
}
#endif

#define TIME_COST_BEGIN(name) \
    uint32_t time_start_##name = /*system_get_curr_time();*/

#define TIME_COST_DONE(name, tag) do { \
    uint32_t time_end_##name = /*system_get_curr_time();*/ \
    int32_t time_diff_val; \
    if (time_end_##name >= time_start_##name) { \
        time_diff_val = (int32_t)(time_end_##name - time_start_##name); \
    } else { \
        /* Handle wrap-around: max value is 83886079 (0x4FFFFFF), then wraps to 0 */ \
        time_diff_val = (int32_t)((83886080U - time_start_##name) + time_end_##name); \
    } \
    printf("%s Time: %d ms\r\n", tag, time_diff_val); \
} while (0)

//https://www.nesdev.org/pal.txt

static nes_color_t nes_palette[]={
#if (NES_COLOR_DEPTH == 32) // ARGB8888
    0xFF757575, 0xFF271B8F, 0xFF0000AB, 0xFF47009F, 0xFF8F0077, 0xFFB0013, 0xFFA70000, 0xFF7F0B00,0xFF432F00, 0xFF004700, 0xFF005100, 0xFF003F17, 0xFF1B3F5F, 0xFF000000, 0xFF000000, 0xFF000000,
    0xFFBCBCBC, 0xFF0073EF, 0xFF233BEF, 0xFF8300F3, 0xFFBF00BF, 0xFF7005B, 0xFFDB2B00, 0xFFCB4F0F,0xFF8B7300, 0xFF009700, 0xFF00AB00, 0xFF00933B, 0xFF00838B, 0xFF000000, 0xFF000000, 0xFF000000,
    0xFFFFFFFF, 0xFF3FBFFF, 0xFF5F97FF, 0xFFA78BFD, 0xFFF77BFF, 0xFFF77B7, 0xFFFF7763, 0xFFFF9B3B,0xFFF3BF3F, 0xFF83D313, 0xFF4FDF4B, 0xFF58F898, 0xFF00EBDB, 0xFF000000, 0xFF000000, 0xFF000000,
    0xFFFFFFFF, 0xFFABE7FF, 0xFFC7D7FF, 0xFFD7CBFF, 0xFFFFC7FF, 0xFFFC7DB, 0xFFFFBFB3, 0xFFFFDBAB,0xFFFFE7A3, 0xFFE3FFA3, 0xFFABF3BF, 0xFFB3FFCF, 0xFF9FFFF3, 0xFF000000, 0xFF000000, 0xFF000000,
#elif (NES_COLOR_DEPTH == 16)
#if (NES_COLOR_SWAP == 0) // RGB565
    0x73AE, 0x20D1, 0x0015, 0x4013, 0x880E, 0x0802, 0xA000, 0x7840,0x4160, 0x0220, 0x0280, 0x01E2, 0x19EB, 0x0000, 0x0000, 0x0000,
    0xBDF7, 0x039D, 0x21DD, 0x801E, 0xB817, 0x000B, 0xD940, 0xCA61,0x8B80, 0x04A0, 0x0540, 0x0487, 0x0411, 0x0000, 0x0000, 0x0000,
    0xFFFF, 0x3DFF, 0x5CBF, 0xA45F, 0xF3DF, 0x0BB6, 0xFBAC, 0xFCC7,0xF5E7, 0x8682, 0x4EE9, 0x5FD3, 0x075B, 0x0000, 0x0000, 0x0000,
    0xFFFF, 0xAF3F, 0xC6BF, 0xD65F, 0xFE3F, 0x0E3B, 0xFDF6, 0xFED5,0xFF34, 0xE7F4, 0xAF97, 0xB7F9, 0x9FFE, 0x0000, 0x0000, 0x0000,
#else // RGB565_SWAP
    0xAE73, 0xD120, 0x1500, 0x1340, 0x0E88, 0x0208, 0x00A0, 0x4078,0x6041, 0x2002, 0x8002, 0xE201, 0xEB19, 0x0000, 0x0000, 0x0000,
    0xF7BD, 0x9D03, 0xDD21, 0x1E80, 0x17B8, 0x0B00, 0x40D9, 0x61CA,0x808B, 0xA004, 0x4005, 0x8704, 0x1104, 0x0000, 0x0000, 0x0000,
    0xFFFF, 0xFF3D, 0xBF5C, 0x5FA4, 0xDFF3, 0xB60B, 0xACFB, 0xC7FC,0xE7F5, 0x8286, 0xE94E, 0xD35F, 0x5B07, 0x0000, 0x0000, 0x0000,
    0xFFFF, 0x3FAF, 0xBFC6, 0x5FD6, 0x3FFE, 0x3B0E, 0xF6FD, 0xD5FE,0x34FF, 0xF4E7, 0x97AF, 0xF9B7, 0xFE9F, 0x0000, 0x0000, 0x0000,
#endif /* NES_COLOR_SWAP */
#endif /* NES_COLOR_DEPTH */
};

__attribute__((section("iram_section"))) nes_t* nes_init(void){
	printf("nes_t size %d\r\n",sizeof(nes_t));
    // 禁用未使用的中断，避免跳转到 Default_Handler
    // SPIS (SPI Slave) 中断在 NES 项目中未使用，禁用以避免意外触发
    //NVIC_DisableIRQ(SPIS_IRQn);
    
    nes_t* nes = (nes_t *)nes_malloc(sizeof(nes_t));
    if (nes == NULL) {
			printf("nes_t size %d\r\n",sizeof(nes_t));
        return NULL;
    }
    nes_memset(nes, 0, sizeof(nes_t));
    // 必须在nes_memset之后设置，否则会被清零
    //nes->nes_draw_data = nes_malloc(sizeof(uint16_t)*NES_WIDTH*NES_HEIGHT);/*(nes_color_t *)(0x22000000);*/
    nes_initex(nes);
    return nes;
}

int nes_deinit(nes_t *nes){
    nes->nes_quit = 1;
    nes_deinitex(nes);
    if (nes){
        nes_free(nes);
        nes = NULL;
    }
    return NES_OK;
}

__attribute__((section("iram_section"))) static inline void nes_palette_generate(nes_t* nes){
    for (uint8_t i = 0; i < 32; i++) {
        nes->nes_ppu.pal.palette[i] = nes_palette[nes->nes_ppu.palette_indexes[i]];
    }
    for (uint8_t i = 1; i < 8; i++){
        nes->nes_ppu.pal.palette[4 * i] = nes->nes_ppu.pal.palette[0];
    }
}

__attribute__((section("iram_section"))) static void nes_render_background_line(nes_t* nes,uint16_t scanline,nes_color_t* draw_data){
    (void)scanline;
    nes_ppu_t* const ppu = &nes->nes_ppu;
    uint8_t p = 0;
    // 使用位操作提取 reg_v 字段
    const uint16_t v_reg = ppu->reg_v.v_reg;
    const uint8_t dx = (uint8_t)(v_reg & 0x1F);  // coarse_x: 位0-4 (5位)
    const uint8_t dy = (uint8_t)((v_reg >> 12) & 0x07);  // fine_y: 位12-14 (3位)
    const uint8_t tile_y = (uint8_t)((v_reg >> 5) & 0x1F);  // coarse_y: 位5-9 (5位)
    uint8_t nametable_id = (uint8_t)((v_reg >> 10) & 0x03);  // nametable: 位10-11 (2位)
    // 优化：缓存 write_latch.x，减少重复访问
    const uint8_t write_latch_x = ppu->write_latch.x;
    int8_t m = 7 - write_latch_x;
    // CTRL_B: 位4 → (ppu_ctrl >> 4) & 0x01
    const uint8_t CTRL_B = (ppu->reg_ctrl.ppu_ctrl >> 4) & 0x01;
    // 优化：缓存常用指针，减少重复数组访问
    const uint8_t* const pattern_table_base = ppu->banks.bank_split.pattern_table[CTRL_B ? 4 : 0];
    const nes_color_t* const bg_palette = ppu->pal.pal_split.background_palette;

    // TIME_COST_BEGIN(draw);
    // TIME_COST_DONE(draw, "Draw Background");
   
    for (uint8_t tile_x = dx; tile_x < 32; tile_x++){
        // 优化：缓存 name_table 指针
        const uint8_t* const name_table_base = ppu->banks.bank_split.name_table[nametable_id];
        const uint8_t pattern_id = name_table_base[tile_x + (tile_y << 5)];
        
        const uint8_t* bit0_p = pattern_table_base + pattern_id * 16;
        const uint8_t* bit1_p = bit0_p + 8;
        const uint8_t bit0 = bit0_p[dy];
        const uint8_t bit1 = bit1_p[dy];
        const uint8_t attribute = name_table_base[960 + ((tile_y >> 2) << 3) + (tile_x >> 2)];
        // 1:D4-D5/D6-D7 0:D0-D1/D2-D3
        // 1:D2-D3/D6-D7 0:D0-D1/D4-D5
        const uint8_t high_bit = ((attribute >> (((tile_y & 2) << 1) | (tile_x & 2))) & 3) << 2;
        for (; m >= 0; m--){
            uint8_t low_bit = ((bit0 >> m) & 0x01) | ((bit1 >> m)<<1 & 0x02);
            uint8_t palette_index = (high_bit & 0x0c) | low_bit;
            draw_data[p++] = bg_palette[palette_index];
        }
        m = 7;
    }
    // TIME_COST_DONE(draw, "Draw Background");

    
    // TIME_COST_BEGIN(draw);
    // TIME_COST_DONE(draw, "Draw Background");
    nametable_id ^= 1;
    // 优化：缓存新的 name_table 指针
    const uint8_t* const name_table_base2 = ppu->banks.bank_split.name_table[nametable_id];
    for (uint8_t tile_x = 0; tile_x <= dx; tile_x++){
        const uint8_t pattern_id = name_table_base2[tile_x + (tile_y << 5)];
        const uint8_t* bit0_p = pattern_table_base + pattern_id * 16;
        const uint8_t* bit1_p = bit0_p + 8;
        const uint8_t bit0 = bit0_p[dy];
        const uint8_t bit1 = bit1_p[dy];
        const uint8_t attribute = name_table_base2[960 + ((tile_y >> 2) << 3) + (tile_x >> 2)];
        // 1:D4-D5/D6-D7 0:D0-D1/D2-D3
        // 1:D2-D3/D6-D7 0:D0-D1/D4-D5
        const uint8_t high_bit = ((attribute >> (((tile_y & 2) << 1) | (tile_x & 2))) & 3) << 2;
        uint8_t skew = 0;
        if (tile_x == dx){
            if (write_latch_x){
                skew = 8 - write_latch_x;
            }else
                break;
        }
        for (; m >= skew; m--){
            const uint8_t low_bit = ((bit0 >> m) & 0x01) | ((bit1 >> m)<<1 & 0x02);
            const uint8_t palette_index = (high_bit & 0x0c) | low_bit;
            draw_data[p++] = bg_palette[palette_index];
        }
        m = 7;
    }
    // TIME_COST_DONE(draw, "Draw Background");
}

__attribute__((section("iram_section")))  static void nes_render_sprite_line(nes_t* nes,uint16_t scanline,nes_color_t* draw_data){
    nes_ppu_t* const ppu = &nes->nes_ppu;
    const nes_color_t background_color = ppu->pal.pal_split.background_palette[0];
    uint8_t sprite[8] = {0};
    uint8_t sprite_numbers = 0;
    // CTRL_H: 位5 → (ppu_ctrl >> 5) & 0x01
    const uint8_t CTRL_H = (ppu->reg_ctrl.ppu_ctrl >> 5) & 0x01;
    const uint8_t sprite_size = CTRL_H ? 16 : 8;

    // 遍历可见的精灵和检测是否精灵溢出
    for (uint8_t i = 0; i < 64; i++){
        if (ppu->oam.sprite_info[i].y >= 0xEF){
            continue;
        }
        uint8_t sprite_y = (uint8_t)(ppu->oam.sprite_info[i].y + 1);
        if (scanline < sprite_y || scanline >= sprite_y + sprite_size){
            continue;
        }
        if (sprite_numbers==8){
            // STATUS_O: 位5 → (ppu_status >> 5) & 0x01
            ppu->reg_status.ppu_status |= 0x20;  // 设置位5
            break;
        }
        sprite[sprite_numbers++]=i;
    }
    // 显示精灵
    // CTRL_S: 位3 → (ppu_ctrl >> 3) & 0x01
    const uint8_t CTRL_S = (ppu->reg_ctrl.ppu_ctrl >> 3) & 0x01;
    for (uint8_t sprite_number = sprite_numbers; sprite_number > 0; sprite_number--){
        const uint8_t sprite_id = sprite[sprite_number-1];
        const sprite_info_t sprite_info = ppu->oam.sprite_info[sprite_id];
        const uint8_t sprite_y = (uint8_t)(sprite_info.y + 1);
        // 使用位操作提取 sprite_info 字段
        const uint8_t tile_index_number = sprite_info.u_tile.tile_index_number;
        const uint8_t attributes = sprite_info.u_attr.attributes;
        // pattern_8x16: 位0 → (tile_index_number & 0x01)
        const uint8_t pattern_8x16 = tile_index_number & 0x01;
        // tile_index_8x16: 位1-7 → (tile_index_number >> 1) & 0x7F
        const uint8_t tile_index_8x16 = (tile_index_number >> 1) & 0x7F;
        // 计算 pattern_table 索引
        const uint8_t pattern_table_idx = CTRL_H ? (pattern_8x16 ? 4 : 0) : (CTRL_S ? 4 : 0);
        // 计算 tile 索引
        const uint8_t tile_idx = CTRL_H ? (tile_index_8x16 << 1) : tile_index_number;
        const uint8_t* sprite_bit0_p = ppu->banks.bank_split.pattern_table[pattern_table_idx] + tile_idx * 16;
        const uint8_t* sprite_bit1_p = sprite_bit0_p + 8;

        uint8_t dy = (uint8_t)(scanline - sprite_y);

        // flip_v: 位7 → (attributes >> 7) & 0x01
        const uint8_t flip_v = (attributes >> 7) & 0x01;
        if (CTRL_H){
            if (flip_v){
                if (dy < 8){
                    sprite_bit0_p +=16;
                    sprite_bit1_p +=16;
                    dy = sprite_size - dy - 1 -8;
                }else{
                    dy = sprite_size - dy - 1;
                }
            }else{
                if (dy > 7){
                    sprite_bit0_p +=16;
                    sprite_bit1_p +=16;
                    dy-=8;
                }
            }
        }else{
            if (flip_v){
                dy = sprite_size - dy - 1;
            }
        }

        const uint8_t sprite_bit0 = sprite_bit0_p[dy];
        const uint8_t sprite_bit1 = sprite_bit1_p[dy];
#if (NES_FRAME_SKIP != 0)
        if(nes->nes_frame_skip_count == 0)
#endif
        {
            uint8_t p = sprite_info.x;
            // flip_h: 位6 → (attributes >> 6) & 0x01
            const uint8_t flip_h = (attributes >> 6) & 0x01;
            // sprite_palette: 位0-1 → (attributes & 0x03)
            const uint8_t sprite_palette = attributes & 0x03;
            // priority: 位5 → (attributes >> 5) & 0x01
            const uint8_t priority = (attributes >> 5) & 0x01;
            if (flip_h){
                for (int8_t m = 0; m <= 7; m++){
                    const uint8_t low_bit = ((sprite_bit0 >> m) & 0x01) | ((sprite_bit1 >> m)<<1 & 0x02);
                    const uint8_t palette_index = (sprite_palette << 2) | low_bit;
                    if (palette_index%4 != 0){
                        if (priority){
                            if (draw_data[p] == background_color){
                                draw_data[p] = ppu->pal.pal_split.sprite_palette[palette_index];
                            }
                        }else{
                            draw_data[p] = ppu->pal.pal_split.sprite_palette[palette_index];
                        }
                    }
                    if (p == 255)
                        break;
                    p++;
                }
            }else{
                for (int8_t m = 7; m >= 0; m--){
                    const uint8_t low_bit = ((sprite_bit0 >> m) & 0x01) | ((sprite_bit1 >> m)<<1 & 0x02);
                    const uint8_t palette_index = (sprite_palette << 2) | low_bit;
                    if (palette_index%4 != 0){
                        if (priority){
                            if (draw_data[p] == background_color){
                                draw_data[p] = ppu->pal.pal_split.sprite_palette[palette_index];
                            }
                        }else{
                            draw_data[p] = ppu->pal.pal_split.sprite_palette[palette_index];
                        }
                    }
                    if (p == 255)
                        break;
                    p++;
                }
            }
        }
        // 检测精灵0命中
        if (sprite_id==0){
            const uint8_t sprite_date = sprite_bit0 | sprite_bit1;
            // MASK_b: 位3 → (ppu_mask >> 3) & 0x01
            const uint8_t MASK_b = (ppu->reg_mask.ppu_mask >> 3) & 0x01;
            // STATUS_S: 位6 → (ppu_status >> 6) & 0x01
            const uint8_t STATUS_S = (ppu->reg_status.ppu_status >> 6) & 0x01;
            if (sprite_date && MASK_b && STATUS_S == 0){
                // nametable: 位10-11 → (v_reg >> 10) & 0x03
                const uint16_t v_reg = ppu->reg_v.v_reg;
                const uint8_t nametable_id = (uint8_t)((v_reg >> 10) & 0x03);
                const uint8_t tile_x = (ppu->oam.sprite_info[0].x) >> 3;
                const uint8_t tile_y = (uint8_t)(scanline >> 3);
                const uint8_t pattern_id = ppu->banks.bank_split.name_table[nametable_id][tile_x + (tile_y << 5)];
                // CTRL_B: 位4 → (ppu_ctrl >> 4) & 0x01 (修正：之前错误写成了位3)
                const uint8_t CTRL_B_local = (ppu->reg_ctrl.ppu_ctrl >> 4) & 0x01;
                const uint8_t* bit0_p = ppu->banks.bank_split.pattern_table[CTRL_B_local ? 4 : 0] + pattern_id * 16;
                const uint8_t* bit1_p = bit0_p + 8;
                const uint8_t background_date = bit0_p[dy] | bit1_p[dy] << 1;
                if (sprite_date & background_date){
                    // STATUS_S: 位6 → 设置位6
                    ppu->reg_status.ppu_status |= 0x40;  // 设置位6
                    // printf("scanline:%d sprite_bit0:%d sprite_bit1:%d sprite_date:%d bit0_p:%d bit1_p:%d background_date:%d \n",
                    // scanline,sprite_bit0,sprite_bit1,sprite_date,bit0_p[dy],bit1_p[dy],background_date);
                }
            }
        }
        
    }
}

// https://www.nesdev.org/wiki/PPU_rendering

// 静态分配扫描线缓冲区，避免在循环中栈溢出
// 大小：NES_WIDTH * sizeof(nes_color_t) = 240 * 4 = 960 字节
// 如果在循环内分配，240次循环会累积超过230KB栈空间，导致HardFault
// 注意：不使用 ram_code 段，因为 ram_code 段主要用于代码，数据初始化可能有问题
// 普通静态变量会在 .bss 段（自动清零）或 .data 段（从 Flash 复制），启动代码会正确处理
static nes_color_t scan_line_buffer[NES_WIDTH] = {0};

// static void nes_background_pattern_test(nes_t* nes){
//     nes_palette_generate(nes);
//     nes_memset(nes->nes_draw_data, nes->nes_ppu.background_palette[0], sizeof(nes_color_t) * NES_DRAW_SIZE);

//     uint8_t nametable_id = 0;
//     for (uint8_t j = 0; j < 16 * 8; j++){
//         uint16_t p = j*NES_WIDTH;
//         uint8_t tile_y = j/8;
//         uint8_t dy = j%8;
//         int8_t m = 7;
//         for (uint8_t i = 0; i < 16; i++){
//             uint8_t tile_x = i;
//             const uint8_t pattern_id = tile_y*16 + tile_x;
//             const uint8_t* bit0_p = nes->nes_ppu.pattern_table[1 ? 4 : 0] + pattern_id * 16;
//             const uint8_t* bit1_p = bit0_p + 8;
//             const uint8_t bit0 = bit0_p[dy];
//             const uint8_t bit1 = bit1_p[dy];
//             const uint8_t attribute = nes->nes_ppu.name_table[nametable_id][960 + ((tile_y >> 2) << 3) + (tile_x >> 2)];
//             const uint8_t high_bit = ((attribute >> (((tile_y & 2) << 1) | (tile_x & 2))) & 3) << 2;
//             for (; m >= 0; m--){
//                 uint8_t low_bit = ((bit0 >> m) & 0x01) | ((bit1 >> m)<<1 & 0x02);
//                 uint8_t palette_index = (high_bit & 0x0c) | low_bit;
//                 nes->nes_draw_data[p++] = nes->nes_ppu.background_palette[palette_index];
//             }
//             m = 7;
//         }
//     }
//     nes_draw(0, 0, NES_WIDTH-1, NES_HEIGHT-1, nes->nes_draw_data);
//     nes_frame(nes);
// }

__attribute__((section("iram_section")))  void nes_run(nes_t* nes){
    printf("mapper:%03d\n",nes->nes_rom.mapper_number);
    printf("prg_rom_size:%d*16kB\n",nes->nes_rom.prg_rom_size);
    printf("chr_rom_size:%d*8kB\n",nes->nes_rom.chr_rom_size);
    printf("mirroring_type:%d\n",nes->nes_rom.mirroring_type);
    printf("four_screen:%d\n",nes->nes_rom.four_screen);
    printf("save_ram:%d\n",nes->nes_rom.save_ram);

    nes_cpu_reset(nes);
    uint64_t frame_cnt = 0;

    while (!nes->nes_quit){
        //printf("cnt:%d\r\n",frame_cnt);
        frame_cnt++;
#if (NES_FRAME_SKIP != 0)
        if(nes->nes_frame_skip_count == 0)
#endif
        {
            nes_palette_generate(nes);
        }
        if ((nes->nes_ppu.reg_mask.ppu_mask & 0x08) == 0){  // MASK_b == 0
#if (NES_FRAME_SKIP != 0)
            if(nes->nes_frame_skip_count == 0)
#endif
            {       
								// printf("nes_memset\r\n");
                nes_memset(nes->nes_draw_data, nes->nes_ppu.pal.pal_split.background_palette[0], sizeof(nes_color_t) * NES_DRAW_SIZE);
								// printf("nes_memset\r\n");

						}
        }
#if (NES_ENABLE_SOUND==1)
        nes_apu_frame(nes);
#endif
       //TIME_COST_BEGIN(f);
        nes_ppu_t* const ppu = &nes->nes_ppu;
        // 缓存循环外不变的寄存器值，减少循环内重复访问
        const uint8_t MASK_b = (ppu->reg_mask.ppu_mask >> 3) & 0x01;  // MASK_b: 位3
        const uint8_t MASK_s = (ppu->reg_mask.ppu_mask >> 4) & 0x01;  // MASK_s: 位4
        // 注意：t_reg 不在循环外缓存，因为它可能在循环内被修改
        // https://www.nesdev.org/wiki/PPU_rendering#Visible_scanlines_(0-239)
        for(nes->scanline = 0; nes->scanline < NES_HEIGHT; nes->scanline++) { // 0-239 Visible frame
            // 缓存循环内重复计算的值
            const uint16_t scanline_offset = (uint16_t)(nes->scanline * NES_WIDTH);
            nes_color_t* const scanline_draw_ptr = nes->nes_draw_data + scanline_offset;
            
            // 优化：使用单个缓冲区处理背景和精灵渲染，减少 memcpy 次数
#if (NES_RAM_LACK == 1)
            if (MASK_b){  // 使用缓存的 MASK_b
#if (NES_FRAME_SKIP != 0)
                if (nes->nes_frame_skip_count == 0)
#endif
                {
                nes_render_background_line(nes, nes->scanline, nes->nes_draw_data + nes->scanline%(NES_HEIGHT/2) * NES_WIDTH);
                }
            }
            if (MASK_s){  // 使用缓存的 MASK_s
                nes_render_sprite_line(nes, nes->scanline, nes->nes_draw_data + nes->scanline%(NES_HEIGHT/2) * NES_WIDTH);
            }
#else
            // 优化1：减少 memcpy 开销 - 如果只渲染背景，直接操作目标缓冲区
            // 优化2：如果同时渲染背景和精灵，才使用中间缓冲区
            if (MASK_b && MASK_s) {
                // 情况1：同时渲染背景和精灵 - 需要中间缓冲区
                memcpy(scan_line_buffer, scanline_draw_ptr, NES_WIDTH * sizeof(nes_color_t));
                
#if (NES_FRAME_SKIP != 0)
                if (nes->nes_frame_skip_count == 0)
#endif
                {
                    nes_render_background_line(nes, nes->scanline, scan_line_buffer);
                }
                nes_render_sprite_line(nes, nes->scanline, scan_line_buffer);
                memcpy(scanline_draw_ptr, scan_line_buffer, NES_WIDTH * sizeof(nes_color_t));
            } else if (MASK_b) {
                // 情况2：只渲染背景 - 直接操作目标缓冲区，避免 memcpy
#if (NES_FRAME_SKIP != 0)
                if (nes->nes_frame_skip_count == 0)
#endif
                {
                    nes_render_background_line(nes, nes->scanline, scanline_draw_ptr);
                }
            } else if (MASK_s) {
                // 情况3：只渲染精灵 - 需要读取一次，但可以优化为只读必要部分
                memcpy(scan_line_buffer, scanline_draw_ptr, NES_WIDTH * sizeof(nes_color_t));
                nes_render_sprite_line(nes, nes->scanline, scan_line_buffer);
                memcpy(scanline_draw_ptr, scan_line_buffer, NES_WIDTH * sizeof(nes_color_t));
            }
#endif
			 //printf("nes_opcode 0\r\n");
            nes_opcode(nes,85); // ppu cycles: 85*3=255
			 //printf("nes_opcode 1\r\n");
            // https://www.nesdev.org/wiki/PPU_scrolling#Wrapping_around
            if (MASK_b){  // 使用缓存的 MASK_b
                // https://www.nesdev.org/wiki/PPU_scrolling#At_dot_256_of_each_scanline
                // 优化：使用位操作替代位域访问，提高性能
                uint16_t v_reg = ppu->reg_v.v_reg;
                uint8_t fine_y = (uint8_t)((v_reg >> 12) & 0x07);  // fine_y: 位12-14
                if (fine_y < 7) {
                    fine_y++;
                    ppu->reg_v.v_reg = (v_reg & (uint16_t)0x8FFF) | ((uint16_t)fine_y << 12);
                } else {
                    ppu->reg_v.v_reg = v_reg & (uint16_t)0x8FFF;  // fine_y = 0
                    uint8_t coarse_y = (uint8_t)((v_reg >> 5) & 0x1F);  // coarse_y: 位5-9
                    if (coarse_y == 29) {
                        coarse_y = 0;
                        ppu->reg_v.v_reg ^= 0x0800;  // 切换 nametable
                    } else if (coarse_y == 31) {
                        coarse_y = 0;
                    } else {
                        coarse_y++;
                    }
                    ppu->reg_v.v_reg = (ppu->reg_v.v_reg & (uint16_t)0xFC1F) | ((uint16_t)coarse_y << 5);
                }
                // https://www.nesdev.org/wiki/PPU_scrolling#At_dot_257_of_each_scanline
                // v: ....A.. ...BCDEF <- t: ....A.. ...BCDEF
                // 注意：t_reg 必须在循环内读取，因为它可能在循环内被修改
                ppu->reg_v.v_reg = (ppu->reg_v.v_reg & (uint16_t)0xFBE0) | (ppu->reg_t.t_reg & (uint16_t)0x041F);
            }

             //printf("nes_opcode 2\r\n");
            nes_opcode(nes,NES_PPU_CPU_CLOCKS-85);
             //printf("nes_opcode 3\r\n");

#if (NES_ENABLE_SOUND==1)
            if (nes->scanline % 66 == 65) nes_apu_frame(nes);
#endif
#if (NES_RAM_LACK == 1)
#if (NES_FRAME_SKIP != 0)
            if(nes->nes_frame_skip_count == 0)
#endif
            {
                if (nes->scanline == NES_HEIGHT/2-1){
                    nes_draw(0, 0, NES_WIDTH-1, NES_HEIGHT/2-1, nes->nes_draw_data);
                }else if(nes->scanline == NES_HEIGHT-1){
                    nes_draw(0, NES_HEIGHT/2, NES_WIDTH-1, NES_HEIGHT-1, nes->nes_draw_data);
                }
            }
#endif
        }
        //TIME_COST_DONE(f, "f");

#if (NES_RAM_LACK == 0)
#if (NES_FRAME_SKIP != 0)
        if(nes->nes_frame_skip_count == 0)
#endif
        {
            // TIME_COST_BEGIN(d);
            nes_draw(20 + 12, 0  , 20 +12 +NES_WIDTH-1  , NES_HEIGHT-1, nes->nes_draw_data);
            // TIME_COST_DONE(d, "d");
        }
#endif
        nes_opcode(nes,NES_PPU_CPU_CLOCKS); //240 Post-render line
        
        ppu->reg_status.ppu_status |= 0x80;  // 设置STATUS_V (位7): Set VBlank flag (241 line)
        if (ppu->reg_ctrl.ppu_ctrl & 0x80) {  // CTRL_V: 位7
            nes->nes_cpu.irq_nmi=1;
        }

        // 优化：将20次单独的调用合并为一次批量执行，减少函数调用开销
        // 原来：20次函数调用，每次113个周期 = 2260个周期，但每次调用都有函数调用开销
        // 现在：1次函数调用，直接执行2260个周期，减少19次函数调用开销
        // 241-260扫描线：垂直空白区域 x20

        // TIME_COST_BEGIN(c);
        nes_opcode(nes, NES_PPU_CPU_CLOCKS * 20);  // 批量执行20个扫描线的CPU周期
        nes->nes_ppu.reg_status.ppu_status = 0;    // Clear:VBlank,Sprite 0,Overflow
        nes_opcode(nes,NES_PPU_CPU_CLOCKS); // Pre-render scanline (-1 or 261)

        if (nes->nes_ppu.reg_mask.mask_bits.MASK_b){
            // https://www.nesdev.org/wiki/PPU_scrolling#During_dots_280_to_304_of_the_pre-render_scanline_(end_of_vblank)
            // v: GHIA.BC DEF..... <- t: GHIA.BC DEF.....
            nes->nes_ppu.reg_v.v_reg = (nes->nes_ppu.reg_v.v_reg & (uint16_t)0x841F) | (nes->nes_ppu.reg_t.t_reg & (uint16_t)0x7BE0);
        }
        nes_frame(nes);
#if (NES_FRAME_SKIP != 0)
        if ( ++nes->nes_frame_skip_count > NES_FRAME_SKIP){
            nes->nes_frame_skip_count = 0;
        }
#endif
    }
}

